//! By convention, main.zig is where your main function lives in the case that
//! you are building an executable. If you are making a library, the convention
//! is to delete this file and start with root.zig instead.

const parseInt = std.fmt.parseInt;
const rbtree = @import("rbtree");
const testfun = @import("testfun.zig");
const dprint = testfun.dprint;
const cstdio = @cImport({
    // See https://github.com/ziglang/zig/issues/515
    @cDefine("_NO_CRT_STDIO_INLINE", "1");
    @cInclude("stdio.h");
});

// const dprint = std.debug.print;
const RB_Node = struct {
    key: i32,
    left: ?*RB_Node,
    right: ?*RB_Node,
    parent: ?*RB_Node,
    color: bool,
};

const FileOpenError = error{
    AccessDenied,
    OutOfMemory,
    FileNotFound,
};

const AllocationError = error{
    OutOfMemory,
};

pub fn out_product(a: [3]f64, b: [3]f64) [3]f64 {
    // @compileError("shits");
    return [3]f64{
        a[1] * b[2] - a[2] * b[1],
        -a[0] * b[2] + a[2] * b[0],
        a[0] * b[1] - a[1] * b[0],
    };
}
/// 这个是个线程安全来包裹类型并返回被包裹的类型
pub fn ThreadSafe(comptime T: type) type {
    return struct {
        mutex: std.Thread.Mutex,
        data: T,

        const Self = @This();

        pub fn init(value: T) Self {
            return .{
                .mutex = std.Thread.Mutex{},
                .data = value,
            };
        }

        pub fn withLock(self: *Self, comptime callback: anytype) @TypeOf(@typeInfo(@TypeOf(callback)).Fn.return_type.?) {
            self.mutex.lock();
            defer self.mutex.unlock();
            return @call(.auto, callback, .{&self.data});
        }

        pub fn get(self: *Self) T {
            self.mutex.lock();
            defer self.mutex.unlock();
            return self.data;
        }

        pub fn set(self: *Self, value: T) void {
            self.mutex.lock();
            defer self.mutex.unlock();
            self.data = value;
        }
    };
}

fn List(comptime T: type) type {
    return struct {
        pos: usize,
        items: []T,
        allocator: std.mem.Allocator,
        fn init(allocator: std.mem.Allocator) !List(T) {
            return .{
                .pos = 0,
                .allocator = allocator,
                .items = try allocator.alloc(T, 4),
            };
        }
    };
}

const Complex_Num = struct {
    r: f64,
    i: f64,
};

const Complex_Num_Euler = struct {
    r: f64,
    theta: f64,
};

const Complex = union(enum) {
    cartesian: Complex_Num,
    euler: Complex_Num_Euler,
};

fn Another_type(x: anytype) type {
    const XType: type = @TypeOf(x);
    if (XType == Complex_Num) {
        return Complex_Num_Euler;
    }
    return Complex_Num;
}

// fn transfer(comptime x: anytype) Another_type(x) {
//     // ...实现
// }
/// 计算傅里叶描述子，也就是做内积
fn compute_dft2(base: anytype, xs: []f64) @TypeOf(base) {
    const BaseType: type = @TypeOf(base);
    // const len: usize = xs.len;
    // var re: BaseType = .{ 0, 0 };
    var re: BaseType = switch (BaseType) {
        Complex_Num => Complex_Num{ .r = 0, .i = 0 },
        Complex_Num_Euler => Complex_Num_Euler{ .r = 0, .theta = base.theta },
        else => @compileError("Unsupported type"),
    };
    if (BaseType == Complex_Num) {
        for (xs) |x| {
            re.i += x * base.i;
            re.r += x * base.r;
        }
    } else if (BaseType == Complex_Num_Euler) {
        for (xs) |x| {
            re.r += x * base.r;
        }
    }
    return re;
}
test compute_dft2 {
    // const a: []f64 = .{};
    const a = [_]f64{ 1, 2, 3 };
    const b: Complex_Num_Euler = .{ .r = 2.1, .theta = 0.43 };
    // const b = Complex_Num_Euler{ 2.1, 0.43 };
    // var aa = a;
    // const aaa: []f64 = blk: {
    //     var aa = a;
    //     break :blk &aa;
    // };
    const re: Complex_Num_Euler = compute_dft2(b, blk: {
        var aa = a;
        break :blk &aa;
    });
    const sum = comptime blk: {
        var acc: f64 = 0;
        for (a) |x| {
            acc += x * b.r;
        }
        break :blk acc;
    };
    try std.testing.expectEqual(sum, re.r);

    // const sum = comptime blk: {
    //     var acc: f64 = 0;
    //     for (a) |x| {
    //         acc += x;
    //     }
    //     break :blk acc;
    // };
    // const result = 2.1 * sum;
    // try std.testing.expectEqual(result, re.r);
    // std.debug.print("2.1 * sum(a) = {}\n", .{result});
}

// fn compute_dft1(comptime T: type, base: T) T {
//
// }

pub fn main() !void {
    const args = try std.process.argsAlloc(std.heap.page_allocator);
    defer std.process.argsFree(std.heap.page_allocator, args);

    // args[0]是程序名，args[1..]才是实际参数
    for (args[0..]) |arg| {
        std.debug.print("参数: {s}\n", .{arg});
    }
    // const a: anyerror!u32 = error.AlwaysError;
    // if (a) |value| {
    //     std.debug.print("type a{}\n", .{value});
    //     // 处理正常值
    // } else |err| {
    //     std.debug.print("type a{}\n", .{err});
    //     // 处理错误（必须显式捕获err）
    // }

    const me_zh = '我';
    // const me_zh = "🖐🏽";
    std.debug.print("{0u} = {0x} \n", .{me_zh}); // 我 = 6211
    // const me_emoji = "\xf0\x9f\x92\xaf";
    // std.debug.print("{s}\n", .{me_emoji});
    // U+1F600
    _ = cstdio.printf("hello\n");

    dprint("cstdio printf type {}\n", .{@TypeOf(cstdio.printf)});
    // '\u{1F600}';

    // const stdin = std.io.getStdIn().reader();
    // stdin.readUntilDelimiterOrEof(&buf, '\n');
    const bytes: *const [5:0]u8 = "hello";
    dprint("bytes type:{}\n", .{@TypeOf(bytes)});
    // const slice: []const u8 = "slice";
    const slice: []const u8 = bytes;
    dprint("slice type:{}\n", .{@TypeOf(slice)});

    const array: [5]u8 = [5]u8{ 1, 2, 3, 4, 5 };
    const array_ptr = &array;
    dprint("array type:{}\n", .{@TypeOf(array_ptr)});
    const array_ptr1: [*]const u8 = &array;
    dprint("array type1:{}\n", .{@TypeOf(array_ptr1)});

    dprint("rb node size:{}\n", .{@sizeOf(RB_Node)});
    std.debug.print("Size of RB_Node: {}\n", .{@sizeOf(RB_Node)});
    std.debug.print("Offset of key: {}\n", .{@offsetOf(RB_Node, "key")});
    std.debug.print("Offset of left: {}\n", .{@offsetOf(RB_Node, "left")});
    std.debug.print("Offset of right: {}\n", .{@offsetOf(RB_Node, "right")});
    std.debug.print("Offset of parent: {}\n", .{@offsetOf(RB_Node, "parent")});
    std.debug.print("Offset of color: {}\n", .{@offsetOf(RB_Node, "color")});
    // fn add(a: i32, b: i32) i32 {
    //         return a + b;
    //     }
    // const result = add(5, 7);
    // std.debug.print("Result of add: {}\n", .{result});
    const tt = .mytest1;
    dprint("tt type:{}\n", .{@TypeOf(tt)});

    const errort: FileOpenError = FileOpenError.OutOfMemory;
    const err_int: u16 = @intFromError(errort);
    const err_int1: u16 = @intFromError(AllocationError.OutOfMemory);
    dprint("err type:{}\n", .{@TypeOf(errort)});

    dprint("value errot:{},{} {}\n", .{ @TypeOf(err_int), err_int, err_int1 });

    var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator: std.mem.Allocator = arena.allocator();

    var list = std.ArrayList(u21).init(allocator);
    defer list.deinit();
    // missing `defer list.deinit();`
    // try list.append('☔');
    dprint("{}\n", .{@sizeOf(@TypeOf(list))});
    // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`)
    std.debug.print("All your {s} are belong to us.\n", .{"codebase"});

    // stdout is for the actual output of your application, for example if you
    // are implementing gzip, then only the compressed bytes should be sent to
    // stdout, not any debugging messages.
    const stdout_file = std.io.getStdOut().writer();
    var bw = std.io.bufferedWriter(stdout_file);
    const stdout = bw.writer();

    try stdout.print("Run `zig build test` to run the tests.\n", .{});

    try bw.flush(); // Don't forget to flush!
}

// test thread_test {
//     const user = .{
//         .power = 9001,
//         .name = "Goku",
//     };
//     var a: u32 = 0;
//     const ptr: *u32 = &a;
//     dprint("ptr type:{}\n", .{@TypeOf(ptr)});
//     dprint("user type:{}\n", .{@TypeOf(user)});
// }

test "simple test" {
    var list = std.ArrayList(i32).init(std.testing.allocator);
    defer list.deinit(); // Try commenting this out and see if zig detects the memory leak!
    try list.append(42);
    try std.testing.expectEqual(@as(i32, 42), list.pop());
}

test "use other module" {
    try std.testing.expectEqual(@as(i32, 150), lib.add(100, 50));
}

test "fuzz example" {
    const Context = struct {
        fn testOne(context: @This(), input: []const u8) anyerror!void {
            _ = context;
            // Try passing `--fuzz` to `zig build test` and see if it manages to fail this test case!
            try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input));
        }
    };
    try std.testing.fuzz(Context{}, Context.testOne, .{});
}

test "parse integers" {
    const input = "123 67 89,99";
    const ally = std.testing.allocator;

    var list = std.ArrayList(u32).init(ally);
    // Ensure the list is freed at scope exit.
    // Try commenting out this line!
    defer list.deinit();

    var it = std.mem.tokenizeAny(u8, input, " ,");
    while (it.next()) |num| {
        const n = try parseInt(u32, num, 10);
        try list.append(n);
    }

    const expected = [_]u32{ 123, 67, 89, 99 };

    for (expected, list.items) |exp, actual| {
        try std.testing.expectEqual(exp, actual);
    }
}
const std = @import("std");

/// This imports the separate module containing `root.zig`. Take a look in `build.zig` for details.
const lib = @import("hello_lib");
// const lib = @import("root.zig");
